pub mod base {
    pub const WINDOW_WIDTH: u32  = 640;
    pub const WINDOW_HEIGHT: u32 = 480;

    pub struct Rect {
        pub x: f64,
        pub y: f64,
        pub w: f64,
        pub h: f64
    }

    impl Rect {
        pub fn to_array(&self) -> [f64;4] { [self.x, self.y, self.w, self.h] }
    }

    pub struct Circle {
        pub x: f64,
        pub y: f64,
        pub r: f64
    }

    impl Circle {
        pub fn to_array(&self) -> [f64;4] { [self.x, self.y, self.r * 2.0, self.r * 2.0] }
    }

    pub enum Color {
        RED     = 0,
        GREEN   = 1,
        BLUE    = 2
    }

    impl Color {
        pub fn get_rgba(&self) -> [f32;4] {
            match self {
                Color::RED      => [1.0, 0.0, 0.0, 1.0],
                Color::GREEN    => [0.0, 1.0, 0.0, 1.0],
                Color::BLUE     => [0.0, 0.0, 1.0, 1.0],
                _               => [1.0, 0.0, 0.0, 1.0],
            }
        }
    }

    pub struct Speed {
        pub speed:  f64,
        pub x_step: f64,
        pub y_step: f64
    }
}

pub mod racket {
    use piston_window::*;
    use crate::base::*;

    const STEP_X_SIZE: f64 = 5.0;
    const STEP_Y_SIZE: f64 = 5.0;
    const RACKET_WIDTH: f64 = 80.0;
    const RACKET_HEIGHT: f64 = 8.0;

    pub struct PingPangRacket {
        pub color:  Color,
        pub rect:   Rect
    }

    impl PingPangRacket {
        // 初始化位置
        pub fn init() -> Self {
            let x = (WINDOW_WIDTH as f64 - RACKET_WIDTH) / 2.0;
            let y = (WINDOW_HEIGHT as f64 - RACKET_HEIGHT) - 30.0;
            PingPangRacket {color: Color::RED, rect: Rect{x: x, y: y, w: RACKET_WIDTH, h: RACKET_HEIGHT}}
        }

        // 绘制球拍
        pub fn draw(&self, ctx: Context, graph: &mut impl Graphics) -> () {
            // // 颜色
            // let red = [1.0, 0.0, 0.0, 1.0];
            // // 窗口上绘制一个方框 [0.0, 0.0, 100.0, 100.0] => [x, y, width, height]
            // let rect = [0.0, 0.0, 100.0, 6.0];
            let red = self.color.get_rgba();
            let rect = self.rect.to_array();
            // 绘制方框
            rectangle(red, rect, ctx.transform, graph);
        }

        fn move_self(&mut self, x_step: i8, y_step: i8) -> () {
            let x_tmp = self.rect.x + x_step as f64 * STEP_X_SIZE;
            if x_tmp < 0.0 {
                self.rect.x = 0.0;
                return ();
            }

            if x_tmp > WINDOW_WIDTH as f64 - RACKET_WIDTH {
                self.rect.x = WINDOW_WIDTH as f64 - RACKET_WIDTH;
                return ();
            }

            self.rect.x = x_tmp;
            self.rect.y = self.rect.y + y_step as f64 * STEP_Y_SIZE;
        }

        pub fn move_left(&mut self) -> () {
            self.move_self(-1i8, 0i8);
        }

        pub fn move_right(&mut self) -> () {
            self.move_self(1i8, 0i8);
        }

        fn move_to_x(&mut self, x_mouse_point: f64) -> () {
            if x_mouse_point < RACKET_WIDTH / 2.0 {
                self.rect.x = 0.0;
                return;
            }

            if x_mouse_point > WINDOW_WIDTH as f64 - RACKET_WIDTH / 2.0 {
                self.rect.x = WINDOW_WIDTH as f64 - RACKET_WIDTH;
                return;
            }

            self.rect.x = x_mouse_point - RACKET_WIDTH / 2.0;
        }


        fn move_to_y(&mut self, y_mouse_point: f64) -> () {
            if y_mouse_point < WINDOW_HEIGHT as f64 / 2.0 {
                self.rect.y = WINDOW_HEIGHT as f64 / 2.0;
                return;
            }
            
            if y_mouse_point > WINDOW_HEIGHT as f64 - RACKET_HEIGHT {
                self.rect.y = WINDOW_HEIGHT as f64 - RACKET_HEIGHT;
                return;
            }

            self.rect.y = y_mouse_point - RACKET_HEIGHT / 2.0;
        }

        pub fn move_to(&mut self, x_mouse_point: f64, y_mouse_point: f64) -> () {
            self.move_to_x(x_mouse_point);
            self.move_to_y(y_mouse_point);
        }
    }
}

pub mod ball {
    use piston_window::*;
    use crate::base::*;
    use crate::racket::*;

    pub struct Ball {
        pub circle: Circle,
        pub speed: Speed,
        pub color: Color
    }

    impl Ball {
        pub fn init() -> Self {
            Ball { 
                circle: Circle{x: 50.0, y: 50.0, r: 10.0}, 
                speed: Speed {speed: 0.2, x_step: 1.0, y_step: 1.0},
                color: Color::GREEN
            }
        }
        
        pub fn move_to(&mut self, racket: &PingPangRacket) -> () {
            // 撞击检测
            if racket.rect.x < self.circle.x && 
                self.circle.x < racket.rect.x + racket.rect.w && 
                (racket.rect.y - self.circle.y - racket.rect.h as f64 <= self.circle.r)
            {
                self.speed.y_step = 0.0 - self.speed.y_step.abs();
            }

            // 最下方的弹射
            let y_tmp = self.circle.y + self.speed.y_step * self.speed.speed;
            if y_tmp > WINDOW_HEIGHT as f64 {
                self.speed.y_step = 0.0 - self.speed.y_step.abs();
                return;
            }
            
            // 最上方的弹射
            if y_tmp < 0.0 {
                self.speed.y_step = self.speed.y_step.abs();
                return;
            }
            
            // 最右方的弹射
            let x_tmp = self.circle.x + self.speed.x_step * self.speed.speed;
            if x_tmp > WINDOW_WIDTH as f64 {
                self.speed.x_step = 0.0 - self.speed.x_step.abs();
                return;
            }
            
            // 最左方的弹射
            if x_tmp < 0.0 {
                self.speed.x_step = self.speed.x_step.abs();
                return;
            }

            self.circle.x = x_tmp;
            self.circle.y = y_tmp;
        }

        // 绘制球
        pub fn draw(&self, ctx: Context, graph: &mut impl Graphics) -> () {
            // // 颜色
            // let red = [1.0, 0.0, 0.0, 1.0];
            // // 窗口上绘制一个方框 [0.0, 0.0, 100.0, 100.0] => [x, y, width, height]
            // let rect = [0.0, 0.0, 100.0, 6.0];
            let red = self.color.get_rgba();
            let circle = self.circle.to_array();
            // 绘制方框
            ellipse(red, circle, ctx.transform, graph);
        } 
    }
}